package com.hzy.app.verticle;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.hzy.app.service.Test;
import com.hzy.app.utils.DbUtils;
import com.hzy.common.handler.CrosHandler;
import com.hzy.common.handler.FailureHandler;
import com.hzy.common.handler.RequestParamsHandler;
import com.hzy.common.handler.ResponsePreHandler;
import com.hzy.context.ApplicationContext;
import com.hzy.context.ApplicationContextUtils;
import com.hzy.context.ContextLoaderInitializer;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpServer;
import io.vertx.core.json.jackson.DatabindCodec;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.ResponseContentTypeHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.handler.TimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class MainVerticle extends AbstractVerticle {

    private final Logger log = LoggerFactory.getLogger(getClass());


    private void initializer() {
        ContextLoaderInitializer.initializer();
        DbUtils.initializer(vertx);
        initializerJackson();
    }

    private static void initializerJackson() {
        ObjectMapper mapper = DatabindCodec.mapper();
        ObjectMapper prettyMapper = DatabindCodec.prettyMapper();

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        prettyMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        mapper.registerModule(javaTimeModule);
        prettyMapper.registerModule(javaTimeModule);
    }


    @Override
    public void start(Promise<Void> startPromise) {

        initializer();

        ApplicationContext applicationContext = ApplicationContextUtils.getRequiredApplicationContext();


        Router mainRouter = Router.router(vertx);

        mainRouter.route().handler(new CrosHandler());
        mainRouter.route().handler(BodyHandler.create().setBodyLimit(10 * 1024 * 1024));
        mainRouter.route().handler(TimeoutHandler.create(10000L));
        mainRouter.route().handler(new RequestParamsHandler());
        mainRouter.route().handler(ResponseContentTypeHandler.create());
        mainRouter.route("/static/*").handler(StaticHandler.create());
        mainRouter.route().handler(new ResponsePreHandler());


        mainRouter.get("/publish").handler(Test::publishMessage);
        mainRouter.get("/select").handler(Test::dbSelect);
        mainRouter.get("/delete").handler(Test::dbRemove);

        Router restAPI = Router.router(vertx);
        restAPI.get("/products/:productID").handler(Test::selectProduct);

        mainRouter.mountSubRouter("/api", restAPI);

        mainRouter.route().failureHandler(new FailureHandler());

        HttpServer server = vertx.createHttpServer();

        server.requestHandler(mainRouter).listen(applicationContext.getProperty("${server.port:8080}", Integer.class), new HttpHandler(applicationContext, startPromise));
    }

    static class HttpHandler implements Handler<AsyncResult<HttpServer>> {
        private final Logger log = LoggerFactory.getLogger(getClass());
        private final ApplicationContext applicationContext;
        private final Promise<Void> startPromise;

        public HttpHandler(ApplicationContext applicationContext, Promise<Void> startPromise) {
            this.applicationContext = applicationContext;
            this.startPromise = startPromise;
        }

        @Override
        public void handle(AsyncResult<HttpServer> res) {
            if (res.succeeded()) {
                startPromise.complete();
                log.info("Verticle [{}] started on port {}", this.getClass().getName(), applicationContext.getProperty("${server.port:8080}", Integer.class));
            } else {
                log.error("{}", res);
                startPromise.fail(res.cause());
            }
        }
    }
}
